﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using StockLib;

namespace StockNet
{
    public class ReceiverDataEventArgs
    {
        private DataType _dataType = DataType.Unknown;

        private KType _kType = KType.None;

        private object _data = null;


        public ReceiverDataEventArgs()
            : this(DataType.Unknown)
        {
        }

        public ReceiverDataEventArgs(DataType dataType)
            : this(dataType, null)
        {
        }

        public ReceiverDataEventArgs(DataType dataType, object data)
            : this(dataType, data, KType.None)
        {
        }

        public ReceiverDataEventArgs(DataType dataType, object data, KType kType)
        {
            this._dataType = dataType;
            this._data = data;
            this._kType = kType;
        }

        public DataType DataType
        {
            get { return this._dataType; }
            set { this._dataType = value; }
        }

        public KType KType
        {
            get { return this._kType; }
            set { this._kType = value; }
        }

        public object Data
        {
            get { return this._data; }
            set { this._data = value; }
        }
    }

    public delegate void ReceiverDataEventHandler(object sender, ReceiverDataEventArgs e);

    public partial class StockReceiver
    {
        #region 接收数据类型
        public const int RCV_REPORT = 0x3f001234;           // 接收行情数据
        public const int RCV_FILEDATA = 0x3f001235;         // 接收文件数据
        public const int RCV_FENBIDATA = 0x3f001301;        // 接收分笔数据
        #endregion

        #region 接收文件类型
        public const int FILE_HISTORY_EX = 2;               // 补日线文件
        public const int FILE_MINUTE_EX = 4;                // 补分钟线文件
        public const int FILE_POWER_EX = 6;                 // 补充除权文件
        public const int FILE_5MINUTE_EX = 81;              // 补5分钟线文件
        public const int FILE_BASE_EX = 0x1000;             // 补基本资料文件
        public const int FILE_NEWS_EX = 0x1002;             // 补新闻类文件
        public const int FILE_HTML_EX = 0x1004;             // 补HTML文件
        #endregion

        #region 接收文件数据包头
        public const UInt32 EKE_HEAD_TAG = 0xffffffff;      //数据头结构标记
        #endregion

        public const int MARKET_SH = 0X4853;
        public const int MARKET_SZ = 0X5A53;

        /// <summary>
        /// 接收数据事件
        /// </summary>
        public event ReceiverDataEventHandler DataChanged;

        /// <summary>
        /// 接收数据的窗口
        /// </summary>
        private Form _form = null;

        /// <summary>
        /// 引擎是否在工作
        /// </summary>
        private bool _working = false;

        /// <summary>
        /// 单例实例
        /// </summary>
        private static StockReceiver _instance = new StockReceiver();

        /// <summary>
        /// 构造函数
        /// </summary>
        private StockReceiver()
        {
        }

        /// <summary>
        /// 创建接收器窗口
        /// </summary>
        /// <returns></returns>
        public void CreateWindow()
        {
            if (_form == null)
            {
                _form = new StockReceiverForm();
            }
        }

        /// <summary>
        /// 销毁接收器窗口
        /// </summary>
        /// <returns></returns>
        public void DestroyWindow()
        {
            if (_form != null)
            {
                _form.Close();
                _form.Dispose();
            }
        }

        /// <summary>
        /// 访问单例
        /// </summary>
        /// <returns></returns>
        public static StockReceiver Instance
        {
            get
            {
                return _instance;
            }
        }

        public bool IsWorking
        {
            get { return this._working; }
        }

        public bool StartWorking()
        {
            if (_form == null)
                return false;

            if (!_working)
            {
                if (StockReceiverDriver.LoadDriver() && StockReceiverDriver.Init(_form.Handle))
                {
                    _working = true;
                }
            }

            return _working;
        }

        public bool StopWorking()
        {
            if (_form == null)
                return false;

            if (_working)
            {
                StockReceiverDriver.Quit(_form.Handle);
                StockReceiverDriver.UnloadDriver();
                _working = false;
            }

            return !_working;
        }

        /// <summary>
        /// 根据消息结构，对接收到的数据进行分析
        /// </summary>
        /// <param name="m">消息结构</param>
        public void OnReceiveData(ref Message m)
        {
            ReceiveData tsdata = (ReceiveData)Marshal.PtrToStructure(m.LParam, typeof(ReceiveData));

            switch ((int)m.WParam)
            {
                case RCV_REPORT:    //行情数据
                    ReceiveReport(ref tsdata);
                    break;

                case RCV_FENBIDATA: //分笔数据
                    //ReceiveFileMinute(ref tsdata);
                    break;

                case RCV_FILEDATA:  //文件数据
                    switch (tsdata.m_wDataType)
                    {
                        case FILE_HISTORY_EX:   // 日线
                            ReceiveFileHistory(ref tsdata, KType.Day);
                            break;

                        case FILE_5MINUTE_EX:   // 5分钟
                            ReceiveFileHistory(ref tsdata, KType.Min5);
                            break;

                        case FILE_MINUTE_EX:    // 分时
                            ReceiveFileMinute(ref tsdata);
                            break;

                        case FILE_POWER_EX:     // 除权
                            ReceiveFilePower(ref tsdata);
                            break;

                        case FILE_BASE_EX:      // 基本资料
                            ReceiveFileBase(ref tsdata);
                            break;

                        case FILE_NEWS_EX:      // 新闻
                            ReceiveFileNews(ref tsdata);
                            break;

                        case FILE_HTML_EX:      // HTML文件
                            break;
                    }

                    break;
            }
        }

        /// <summary>
        /// 收到行情数据
        /// </summary>
        /// <param name="tsdata">通视数据</param>
        private void ReceiveReport(ref ReceiveData tsdata)
        {
            // 保存行情数据
            SortedList<string, ReportData> reportData = new SortedList<string, ReportData>();
            for (int i = 0; i < tsdata.m_nPacketNum; i++)
            {
                ReceiveReportPack pack = (ReceiveReportPack)Marshal.PtrToStructure(new IntPtr((int)tsdata.m_pData + i * 158 - 2), typeof(ReceiveReportPack));
                if (!string.IsNullOrEmpty(pack.m_szLabel))
                {
                    ReportData data = new ReportData();
                    ConvertReportPackToInternal(ref pack, data);
                    reportData.Add(data.CodeKey, data);
                }
            }

            // 更新容器
            StockContainer.Instance.UpdateByReportData(reportData);

            // 通知行情数据接收
            OnDataChanged(this, new ReceiverDataEventArgs(DataType.Report));
        }

        /// <summary>
        /// 补充K线数据
        /// </summary>
        /// <param name="tsdata">通视数据</param>
        /// <param name="kType">K线类型</param>
        private void ReceiveFileHistory(ref ReceiveData tsdata, KType kType)
        {
            List<KData> kdata = new List<KData>();
            Stock stock = new Stock();
            ReceiveHistoryFilePack pack;
            ReceiveFileHead head;

            try
            {
                int packSize = Marshal.SizeOf(typeof(ReceiveHistoryFilePack));
                for (int i = 0; i < tsdata.m_nPacketNum; i++)
                {
                    IntPtr pointer = new IntPtr((int)tsdata.m_pData + i * packSize);
                    head = (ReceiveFileHead)Marshal.PtrToStructure(pointer, typeof(ReceiveFileHead));
                    pack = (ReceiveHistoryFilePack)Marshal.PtrToStructure(pointer, typeof(ReceiveHistoryFilePack));
                    if (head.m_dwHeadTag == EKE_HEAD_TAG)
                    {
                        if (kdata.Count > 0)
                        {
                            //保存到文件
                            StockDatabase.Instance.SaveKData(stock, kdata, kType);
                            kdata.Clear();
                        }

                        // 保存股票信息
                        stock.Market = ConvertMarketToInternal(head.m_wMarket);
                        stock.Code = head.m_szLabel;
                    }
                    else
                    {
                        // 转换数据包
                        KData k = new KData();
                        ConvertHistoryFilePackToInternal(ref pack, k);
                        kdata.Add(k);
                    }
                }
                if (kdata.Count > 0)
                {
                    //保存到文件
                    StockDatabase.Instance.SaveKData(stock, kdata, kType);
                    kdata.Clear();
                }


                // 通知K线数据接收
                OnDataChanged(this, new ReceiverDataEventArgs(DataType.K, null, kType));
            }
            catch (Exception ex)
            {
                Trace.WriteLine(string.Format("文件操作出错 股票代码:{0} 异常信息:{1}", stock.Code, ex.ToString()));
                throw ex;
            }
        }

        /// <summary>
        /// 补充分时数据
        /// </summary>
        /// <param name="tsdata">通视数据</param>
        private void ReceiveFileMinute(ref ReceiveData tsdata)
        {
            List<MinuteData> kdata = new List<MinuteData>();
            Stock stock = new Stock();
            ReceiveMinuteFilePack pack;
            ReceiveFileHead head;

            try
            {
                int packSize = Marshal.SizeOf(typeof(ReceiveMinuteFilePack));
                for (int i = 0; i < tsdata.m_nPacketNum; i++)
                {
                    IntPtr pointer = new IntPtr((int)tsdata.m_pData + i * packSize);
                    head = (ReceiveFileHead)Marshal.PtrToStructure(pointer, typeof(ReceiveFileHead));
                    pack = (ReceiveMinuteFilePack)Marshal.PtrToStructure(pointer, typeof(ReceiveMinuteFilePack));
                    if (head.m_dwHeadTag == EKE_HEAD_TAG)
                    {
                        if (kdata.Count > 0)
                        {
                            //保存到文件
                            StockDatabase.Instance.SaveMinuteData(stock, kdata);
                            kdata.Clear();
                        }

                        // 保存股票信息
                        stock.Market = ConvertMarketToInternal(head.m_wMarket);
                        stock.Code = head.m_szLabel;
                    }
                    else
                    {
                        // 转换数据包
                        MinuteData k = new MinuteData();
                        ConvertMinuteFilePackToInternal(ref pack, k);
                        kdata.Add(k);
                    }
                }
                if (kdata.Count > 0)
                {
                    //保存到文件
                    StockDatabase.Instance.SaveMinuteData(stock, kdata);
                    kdata.Clear();
                }

                // 通知分时数据接收
                OnDataChanged(this, new ReceiverDataEventArgs(DataType.Minute));
            }
            catch (Exception ex)
            {
                Trace.WriteLine(string.Format("文件操作出错 股票代码:{0} 异常信息:{1}", stock.Code, ex.ToString()));
                throw ex;
            }
        }

        /// <summary>
        /// 补充分笔数据
        /// </summary>
        /// <param name="tsdata">通视数据</param>
        private void ReceiveFileTick(ref ReceiveData tsdata)
        {
            List<MinuteData> kdata = new List<MinuteData>();
            Stock stock = new Stock();
            ReceiveMinuteFilePack pack;
            ReceiveFileHead head;

            try
            {
                int packSize = Marshal.SizeOf(typeof(ReceiveMinuteFilePack));
                for (int i = 0; i < tsdata.m_nPacketNum; i++)
                {
                    IntPtr pointer = new IntPtr((int)tsdata.m_pData + i * packSize);
                    head = (ReceiveFileHead)Marshal.PtrToStructure(pointer, typeof(ReceiveFileHead));
                    pack = (ReceiveMinuteFilePack)Marshal.PtrToStructure(pointer, typeof(ReceiveMinuteFilePack));
                    if (head.m_dwHeadTag == EKE_HEAD_TAG)
                    {
                        if (kdata.Count > 0)
                        {
                            //保存到文件
                            StockDatabase.Instance.SaveTickData(stock, kdata);
                            kdata.Clear();
                        }

                        // 保存股票信息
                        stock.Market = ConvertMarketToInternal(head.m_wMarket);
                        stock.Code = head.m_szLabel;
                    }
                    else
                    {
                        // 转换数据包
                        MinuteData k = new MinuteData();
                        ConvertMinuteFilePackToInternal(ref pack, k);
                        kdata.Add(k);
                    }
                }
                if (kdata.Count > 0)
                {
                    //保存到文件
                    StockDatabase.Instance.SaveTickData(stock, kdata);
                    kdata.Clear();
                }

                // 通知分笔数据接收
                OnDataChanged(this, new ReceiverDataEventArgs(DataType.Tick));
            }
            catch (Exception ex)
            {
                Trace.WriteLine(string.Format("文件操作出错 股票代码:{0} 异常信息:{1}", stock.Code, ex.ToString()));
                throw ex;
            }
        }

        /// <summary>
        /// 补充除权数据
        /// </summary>
        /// <param name="data">接收数据结构</param>
        private void ReceiveFilePower(ref ReceiveData data)
        {
            List<XdrData> kdata = new List<XdrData>();
            Stock stock = new Stock();
            ReceivePowerFilePack pack;
            ReceiveFileHead head;

            try
            {
                int packSize = Marshal.SizeOf(typeof(ReceivePowerFilePack));
                for (int i = 0; i < data.m_nPacketNum; i++)
                {
                    IntPtr pointer = new IntPtr((int)data.m_pData + i * packSize);
                    head = (ReceiveFileHead)Marshal.PtrToStructure(pointer, typeof(ReceiveFileHead));
                    pack = (ReceivePowerFilePack)Marshal.PtrToStructure(pointer, typeof(ReceivePowerFilePack));
                    if (head.m_dwHeadTag == EKE_HEAD_TAG)
                    {
                        if (kdata.Count > 0)
                        {
                            //保存到文件
                            StockDatabase.Instance.SaveXdrData(stock, kdata);
                            kdata.Clear();
                        }

                        // 保存股票信息
                        stock.Market = ConvertMarketToInternal(head.m_wMarket);
                        stock.Code = head.m_szLabel;
                    }
                    else
                    {
                        // 转换数据包
                        XdrData k = new XdrData();
                        ConvertPowerFilePackToInternal(ref pack, k);
                        kdata.Add(k);
                    }
                }
                if (kdata.Count > 0)
                {
                    //保存到文件
                    StockDatabase.Instance.SaveXdrData(stock, kdata);
                    kdata.Clear();
                }

                // 通知除权数据接收
                OnDataChanged(this, new ReceiverDataEventArgs(DataType.XDR));
            }
            catch (Exception ex)
            {
                Trace.WriteLine(string.Format("文件操作出错 股票代码:{0} 异常信息:{1}", stock.Code, ex.ToString()));
                throw ex;
            }
        }

        /// <summary>
        /// 接收到基本资料文本文件
        /// </summary>
        /// <param name="tsdata">通视数据</param>
        private void ReceiveFileBase(ref ReceiveData tsdata)
        {
            Stock stock = new Stock();
            FileStream fileStream = null;
            FileInfo fileInfo = null;

            try
            {
                string[] fileNameParts = tsdata.m_szFileName.Split(new char[] { '.' });
                stock.Market = ConvertMarketToInternal(Convert.ToUInt16(tsdata.m_dwAttrib));
                stock.Code = fileNameParts[0];

                fileInfo = new FileInfo(StockDatabase.Instance.GetFileName(stock, DataType.Base));
                if (!fileInfo.Directory.Exists)
                    Directory.CreateDirectory(fileInfo.Directory.FullName);
                fileStream = new FileStream(fileInfo.FullName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);

                byte[] buffer = new byte[tsdata.m_dwLen];
                Marshal.Copy(tsdata.m_pData, buffer, 0, buffer.Length);
                fileStream.Write(buffer, 0, buffer.Length);

                // 通知基本资料数据接收
                OnDataChanged(this, new ReceiverDataEventArgs(DataType.Base));
            }
            catch (Exception ex)
            {
                Trace.WriteLine(string.Format("文件操作出错 股票代码:{0} 异常信息:{1}", stock.Code, ex.ToString()));
                throw ex;
            }
            finally
            {
                if (fileStream != null)
                {
                    fileStream.Close();
                    fileStream.Dispose();
                    fileStream = null;
                }
            }
        }

        /// <summary>
        /// 接收到新闻文本文件
        /// </summary>
        /// <param name="tsdata">通视数据</param>
        private void ReceiveFileNews(ref ReceiveData tsdata)
        {
            Stock stock = new Stock();
            FileStream fileStream = null;
            FileInfo fileInfo = null;

            try
            {
                fileInfo = new FileInfo(StockDatabase.Instance.GetFileName3(DataType.News, tsdata.m_szFileName));
                if (!fileInfo.Directory.Exists)
                    Directory.CreateDirectory(fileInfo.Directory.FullName);
                fileStream = new FileStream(fileInfo.FullName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);

                byte[] buffer = new byte[tsdata.m_dwLen];
                Marshal.Copy(tsdata.m_pData, buffer, 0, buffer.Length);
                fileStream.Write(buffer, 0, buffer.Length);

                // 通知新闻资讯数据接收
                OnDataChanged(this, new ReceiverDataEventArgs(DataType.News));
            }
            catch (Exception ex)
            {
                Trace.WriteLine(string.Format("文件操作出错 股票代码:{0} 异常信息:{1}", stock.Code, ex.ToString()));
                throw ex;
            }
            finally
            {
                if (fileStream != null)
                {
                    fileStream.Close();
                    fileStream.Dispose();
                    fileStream = null;
                }
            }
        }

        private void ConvertPackToInternal(ref object pack, object data)
        {
            if (pack is ReceiveReportPack && data is ReportData)
            {
                //ConvertReportPackToInternal(ref (ReceiveReportPack)pack, (ReportData)data);
            }
        }

        private void ConvertReportPackToInternal(ref ReceiveReportPack reportPack, ReportData reportData)
        {
            reportData.Market = ConvertMarketToInternal(reportPack.m_wMarket);
            reportData.Code = reportPack.m_szLabel;
            reportData.Name = reportPack.m_szName;
            reportData.Time = reportPack.m_time;
            reportData.Close = reportPack.m_fLastClose;
            reportData.Open = reportPack.m_fOpen;
            reportData.High = reportPack.m_fHigh;
            reportData.Low = reportPack.m_fLow;
            reportData.New = reportPack.m_fNewPrice;
            reportData.Volume = reportPack.m_fVolume;
            reportData.Amount = reportPack.m_fAmount;
            reportData.BuyPrice[0] = reportPack.m_fBuyPrice[0];
            reportData.BuyPrice[1] = reportPack.m_fBuyPrice[1];
            reportData.BuyPrice[2] = reportPack.m_fBuyPrice[2];
            reportData.BuyPrice[3] = reportPack.m_fBuyPrice4;
            reportData.BuyPrice[4] = reportPack.m_fBuyPrice5;
            reportData.BuyVolume[0] = reportPack.m_fBuyVolume[0];
            reportData.BuyVolume[1] = reportPack.m_fBuyVolume[1];
            reportData.BuyVolume[2] = reportPack.m_fBuyVolume[2];
            reportData.BuyVolume[3] = reportPack.m_fBuyVolume4;
            reportData.BuyVolume[4] = reportPack.m_fBuyVolume5;
            reportData.SellPrice[0] = reportPack.m_fSellPrice[0];
            reportData.SellPrice[1] = reportPack.m_fSellPrice[1];
            reportData.SellPrice[2] = reportPack.m_fSellPrice[2];
            reportData.SellPrice[3] = reportPack.m_fSellPrice4;
            reportData.SellPrice[4] = reportPack.m_fSellPrice5;
            reportData.SellVolume[0] = reportPack.m_fSellVolume[0];
            reportData.SellVolume[1] = reportPack.m_fSellVolume[1];
            reportData.SellVolume[2] = reportPack.m_fSellVolume[2];
            reportData.SellVolume[3] = reportPack.m_fSellVolume4;
            reportData.SellVolume[4] = reportPack.m_fSellVolume5;/**/
        }

        private void ConvertHistoryFilePackToInternal(ref ReceiveHistoryFilePack historyPack, KData kData)
        {
            kData.Time = historyPack.m_time;
            kData.Close = historyPack.m_fClose;
            kData.Open = historyPack.m_fOpen;
            kData.High = historyPack.m_fHigh;
            kData.Low = historyPack.m_fLow;
            kData.Volume = historyPack.m_fVolume;
            kData.Amount = historyPack.m_fAmount;
            kData.Advance = historyPack.m_wAdvance;
            kData.Decline = historyPack.m_wDecline;
        }

        private void ConvertMinuteFilePackToInternal(ref ReceiveMinuteFilePack minutePack, MinuteData minuteData)
        {
            minuteData.Time = minutePack.m_time;
            minuteData.Price = minutePack.m_fPrice;
            minuteData.Volume = minutePack.m_fVolume;
            minuteData.Amount = minutePack.m_fAmount;
        }

        private void ConvertPowerFilePackToInternal(ref ReceivePowerFilePack powerPack, XdrData xdrData)
        {
            xdrData.Time = powerPack.m_time;
            xdrData.Give = powerPack.m_fGive;
            xdrData.Pei = powerPack.m_fPei;
            xdrData.PeiPrice = powerPack.m_fPeiPrice;
            xdrData.Profit = powerPack.m_fProfit;
        }

        public StockMarket ConvertMarketToInternal(UInt16 wMarket)
        {
            switch (wMarket)
            {
                case MARKET_SH:
                    return StockMarket.SHSE;

                case MARKET_SZ:
                    return StockMarket.SZSE;
            }

            return StockMarket.Unknown;
        }

        protected void OnDataChanged(object sender, ReceiverDataEventArgs e)
        {
            if (DataChanged != null)
            {
                DataChanged(sender, e);
            }
        }

        class StockReceiverForm : Form
        {
            private const int RCV_DATA_MSG = 0x8001;            // 指定使用的消息

            protected override CreateParams CreateParams
            {
                get
                {
                    CreateParams cp = base.CreateParams;
                    cp.Height = 0;
                    cp.Width = 0;
                    cp.Style |= 0x40000000;
                    cp.ExStyle |= 0x08000000;
                    cp.ClassStyle |= 0x0200;
                    return cp;
                }
            }

            //重载缺省窗口过程，捕获数据接收事件，然后进行数据处理
            //数据处理时，将触发数据更新事件
            protected override void DefWndProc(ref Message msg)
            {
                switch (msg.Msg)
                {
                    case RCV_DATA_MSG:
                        StockReceiver.Instance.OnReceiveData(ref msg);
                        return;
                }

                base.DefWndProc(ref msg);
            }
        }
    }
}
